package com.avcompris.util;

import java.lang.reflect.InvocationTargetException;

import com.avcompris.common.annotation.Nullable;
import com.avcompris.lang.NullArgumentException;

/**
 * utilities on exceptions.
 * 
 * @author David Andriana Copyright Avantage Compris SARL 2008-2009 ©
 */
public abstract class ExceptionUtils extends AbstractUtils {

	/**
	 * return the target exception if available and if it is an
	 * {@link Exception}, or return the invocation target exception itself
	 * otherwise.
	 * 
	 * @param invocationTargetException the exception catched on a reflection
	 *            invocation
	 * @return the target exception, if it is an exception
	 */
	public static Throwable getInvocationTargetThrowable(
			final InvocationTargetException invocationTargetException) {

		nonNullArgument(invocationTargetException, "invocationTargetException");

		final Throwable cause = invocationTargetException.getTargetException();

		if (cause != null) {

			return cause;
		}

		return invocationTargetException;
	}

	/**
	 * return the target exception if available and if it is an
	 * {@link Exception}, or return the invocation target exception itself
	 * otherwise.
	 * 
	 * @param invocationTargetException the exception catched on a reflection
	 *            invocation
	 * @return the target exception, if it is an exception
	 */
	public static Exception getInvocationTargetException(
			final InvocationTargetException invocationTargetException) {

		nonNullArgument(invocationTargetException, "invocationTargetException");

		final Throwable cause = invocationTargetException.getTargetException();

		if (cause != null) {

			if (cause instanceof RuntimeException) {

				return (RuntimeException) cause;

			} else if (cause instanceof Exception) {

				return (Exception) cause;

			} else if (cause instanceof Error) {

				throw (Error) cause;

			} else {

				return new RuntimeException(cause);
			}
		}

		return invocationTargetException;
	}

	/**
	 * return the target exception if available and if it is an
	 * {@link RuntimeException}, or return a new {@link RuntimeException}
	 * chained with the target exception otherwise.
	 * 
	 * @param invocationTargetException the exception catched on a reflection
	 *            invocation
	 * @return the target exception, if it is an {@link RuntimeException}, or a
	 *         newly created chained {@link RuntimeException} otherwise.
	 */
	public static RuntimeException getRuntimeTargetException(
			final InvocationTargetException invocationTargetException) {

		nonNullArgument(invocationTargetException, "invocationTargetException");

		final Throwable cause = invocationTargetException.getTargetException();

		if (cause == null) {

			return new RuntimeException(invocationTargetException);

		} else if (cause instanceof RuntimeException) {

			return (RuntimeException) cause;

		} else {

			return new RuntimeException(cause);
		}
	}

	/**
	 * check an argument is not <tt>null</tt>.
	 * 
	 * @param <T> the class of the value to check
	 * @param value the value to check
	 * @param message the error message to raise if the value is <tt>null</tt>
	 * @return the value itself, if not <tt>null</tt>
	 * @throws NullArgumentException if the value is <tt>null</tt>
	 */
	public static <T> T nonNullArgument(
			@Nullable final T value,
			@Nullable final Object message) {

		if (value == null) {
			throw new NullArgumentException(String.valueOf(message));
		}

		return value;
	}

	/**
	 * check an argument is not <tt>null</tt>.
	 * 
	 * @param <T> the class of the value to check
	 * @param value the value to check
	 * @return the value itself, if not <tt>null</tt>
	 * @throws NullArgumentException if the value is <tt>null</tt>
	 */
	public static <T> T nonNullArgument(
			@Nullable final T value) {

		nonNullArgument(value, "x");

		return value;
	}
}
